<?php

$args = arguments($argv); 

//echo print_r($args);
//p( escape($args['input'][1]) );
//die;
//slashit("/teste/total/");

if (!isCli()) {
  echo "this is only for php cli";
  die;
}

p("");
p("[[ move-torrent.php ]]");
    
if ($argc==1) {
  showHelp();
} else {
  if (hasCommand('list')) { 
    listTorrents(false);
  } else if (hasCommand('listdirs')) {
    listTorrents(true);
  } else if (hasCommand('help')) {
    showHelp();
  } else if (hasCommand('rpccommands')) {
    listCommands();
  } else if (hasCommand('state')) {
    if (count($args['input']) < 2) {
      p("state needs <torrent name>");
      showHelp();
      die;
    }
    showState($args['input'][1]);
  } else if (hasCommand('torrentid')) {
    if (count($args['input']) < 2) {
      p("torrentid needs <torrent name>");
      showHelp();
      die;
    }
    showID($args['input'][1]);
  } else if (hasCommand('move') or hasCommand('rename')) {
    if (count($args['input']) < 3) {
      p("move needs <torrent name> and <file/directory>");
      showHelp();
      die;
    }
    moveTorrent($args['input'][1],$args['input'][2],hasCommand('rename'));
  } else {
    showHelp();
  }
}

function showHelp() {
  p("");
  p("Usage: [sudo] php move-torrent.php <command> [<flag>] [<torrent name>] [<destination directory>]");
  p("");
  p("Options:");
  p("  --list           List torrents");
  p("  --listdirs       List torrents and directories");
  p("  --help           This help");
  p("  --rpccommands    List all xmlrpc commands");
  p("  --state          Show torrent current state (needs <torrent name>)");
  p("  --torrentid      Show torrent ID (needs <torrent name>)");
  p("  --move           Move torrent (needs <torrent name> and <destination directory> arguments)");
  p("");
  
  p("Flags:");
  p("  -t             Don't move files, just change torrent");
  p("  -m             Don't touch torrent, just move files");
  p("  -s             Simulate: don't do anything, just show what's going to happen");
  p("");
    
  p("Examples: php move-torrent.php --list");
  p("          php move-torrent.php --move -s Lynda.com.Up.and.Running.with.Evernote.for.Windows-iNKiSO /tank/MediaCenter");
  p("          sudo php move-torrent.php --move -t 'IObit Malware Fighter PRO v1.4.0.12 Incl Serial {ph4nt0m}' '/bigHDD/Media Center'");
  p("");
  p("");
}

function showState($torrent) {
  p("");
  p("torrent state: ".strtoupper(getState($torrent)));
  p("");
}

function getState($torrent) {
  $id = getTorrentID($torrent);
  if (!$id) {
    die;
  }
  $state = Execute('d.state '.$id,'64-bit integer',": ");
  if ($state[0] == 0) {
    return "paused/stopped";
  } else {
    return "started";
  }
}

function getTorrentID($torrent) {
  $ids = Execute('download_list','Index ',"'");
  foreach($ids as $id) {
    $torrentName = Execute('d.name '.$id,'String: ',"'");
    $torrentName = $torrentName[0];
    //p( $torrentName . " - " . $torrent );
    if (($torrentName===$torrent)) {
      return $id;
    }
  }
  p('torrent "'.$torrent.'" was not found');
  return false; 
}

function listCommands($dir) {
  p("");
  $ids = Execute('system.listMethods','Index ',"'");
  foreach($ids as $id) {
    p($id);
  }    
}

function showID($t) {
  p( getTorrentID($t) );
}

function listTorrents($dir) {
  p("");
  $ids = Execute('download_list','Index ',"'");
  foreach($ids as $id) {
    $torrentName = Execute('d.name '.$id,'String: ',"'");
    $torrentDir = Execute('d.directory '.$id,'String: ',"'");
    
    if ($dir) {
      $t = $torrentName[0].' (dir: '.$torrentDir[0].')';
    } else {
      $t = $torrentName[0];
    }

    p($t);
  }
}

function moveTorrent($myTorrent,$baseDir,$rename) {
  $id = getTorrentID($myTorrent);
  if (!$id) {
    die;
  }
  if (!is_dir ( $baseDir ) and !is_dir ( escape($baseDir) ) and !$rename) {
    p("Directory ".$baseDir." does not exists.");
    die;
  }
  $torrentName = Execute('d.name '.$id,'String: ',"'");
  $torrentName = $torrentName[0];
  $torrentDir = Execute('d.directory '.$id,'String: ',"'");
  $torrentDir = $torrentDir[0];
  $newBaseDir = $baseDir;              

  $pos = strpos($torrentDir,$torrentName);
  if ($pos===false) {
    $torrentDir = slashit($torrentDir) . $torrentName;
  } else {
    if ((slashit($baseDir) . $torrentName) === $torrentDir) {
      p("torrent is already in '$torrentDir'");
      die;
    }
  }
  
  $wasStarted = getState($torrentName)=="started";

  if (!is_writable ( $torrentDir )) {
    p("1: looks like you don't have enough rights on \"$torrentDir\"");
    if ( !hasFlag('s') and !hasFlag('t') ) {
      die;
    }  
  }
  if (!is_writable ( $newBaseDir ) and !$rename) {
    p("2: looks like you don't have enough rights on \"$newBaseDir\"");
    if ( !hasFlag('s') and !hasFlag('t') ) {
      die;
    }
  }
      
  if ($wasStarted) {
    p("stopping torrent...");
    if (!hasFlag('s') and !hasFlag('m')) {
      Execute('d.stop '.$id,'String: ',"'");
      while (getState($torrentName)=="started") {
        sleep(1);
      }
    }  
    p("torrent stopped");
  }
      
  p("setting new torrent directory");
  if (!hasFlag('s') and !hasFlag('m')) {    
    Execute('d.set_directory '.$id.' '.escape($newBaseDir),'String: ',"'");
    //p("Execute('d.set_directory '.$id.' '.escape($newBaseDir),'String: ','#'"); 
  }

  p("moving file(s)");
  p( " ".$torrentDir .' -> '. $newBaseDir );
  if (!hasFlag('s') and !hasFlag('t')) {
    try{
      $error = shell_exec( 'mv "'. escape($torrentDir) .'" "'. escape($newBaseDir) .'"' );
    } catch(Exception $e){
      p( "exception" .$e->getMessage() );
      
    }
  }
    
  if ($wasStarted) {
    p("starting torrent");            
    if (!hasFlag('s') and !hasFlag('m')) {
      Execute('d.start '.$id,'String: ',"'");
    }
  }
}

function Execute($command, $lineID, $stringDelimiter) {
global $debug;
  //p('executing command: xmlrpc localhost '.$command);
  $output = shell_exec('xmlrpc localhost '.$command);
  $output = explode("\n",$output);
  $ids = array();

  foreach ($output as $line) {
    $pos = strpos($line,$lineID);
    if ($lineID and !($pos===false)) {
      if ($stringDelimiter) {
        $line = explode($stringDelimiter,$line);
        $ids[] = $line[1];
      } else {
        $ids[] = $line;
      }
    } else if(!$lineID) {
      $ids[] = $line;
    }
  }
  
  return $ids;
}

function p($s) {
global $debug;
  
  $simulating = hasFlag('s') ? ' (simulation)' : '';
  echo $s.$simulating."\n";
}

function isCli() {
  return php_sapi_name()==="cli";
}

function arguments ( $args )
{
    $ret = array(
        'input'    => array(),
        'commands' => array(),
        'flags'    => array()
    );

    foreach ( $args as $arg ) {

        // Is it a command? (prefixed with --)
        if ( substr( $arg, 0, 2 ) === '--' ) {

            $value = preg_split( '/[= ]/', $arg, 2 );
            $com   = substr( array_shift($value), 2 );
            $value = join($value);

            $ret['commands'][$com] = !empty($value) ? $value : true;
            continue;

        }

        // Is it a flag? (prefixed with -)
        if ( substr( $arg, 0, 1 ) === '-' ) {
            $flag = substr( $arg, 1 ); 
            $ret['flags'][$flag] = 1;
            continue;
        }

        $ret['input'][] = $arg;
        continue;

    }

    return $ret;
}

function hasCommand($command) {
global $args;
  return array_key_exists('commands', $args) and array_key_exists($command, $args['commands']);
}

function hasFlag($command) {
global $args;
  return isset( $args['flags'][$command] ) and $args['flags'][$command];
}
    
function escape( $s ) {
  $backSlash = chr(92);
  $slash = chr(47);
  $last = "";
  $current = "";
  $chars = array(  "!" => 1
                  ,"@" => 1
                  ,"#" => 1
                  ,"." => 1
                  ,"$" => 1
                  ,"%" => 1
                  ,"^" => 1
                  ,"(" => 1
                  ," " => 1
                  ,")" => 1 );

  $n = "";
  for ($i = 0; $i < strlen($s); $i++) {
    $current = $s[$i];
    if( isset($chars[$current]) and !($last === $backSlash) ) {
      $n = $n . $backSlash;
    }
    $last = $current;
    $n = $n . $current;
  }
    
  //return $n;
  return $s; /// as a matter of fact? does nothing yet, need to know exactly what must be escaped for real
}

function slashit($s) {
  $s = trim($s);
  if (!($s[strlen($s)-1] === "/")) {
    $s = $s . "/";
  } 
  return $s;
}
